package xj.graph2d;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import xj.graph2d.graph.Graph;
import xj.graph2d.graph.Subgraph;
import xj.meta.AssociationElement;
import xj.meta.ModelElement;
import xj.meta.ReferenceElement;

public class CloneManager {

  public static void startSession() {
    inSession = true;
    compIdMap.clear();
    modelIdMap.clear();
    compMap.clear();
    modelMap.clear();
    edges.clear();
  }

  public static void endSession(GraphDocument graph) { 
    //addClonedNodes(graph);
    resolveClonedReferences(graph);
    inSession = false;
  }
  
  public static void addShape(String oldid, DrawShape shape) {
    if (inSession && 
	oldid != null && 
	shape != null) {
      ModelElement model = shape.getModel();
      if (model != null) { 
	String newid = model.getId();
	modelIdMap.put(oldid, newid);
	modelMap.put(newid, model); 
      }
    }
  }

  public static void addGraphNode(String oldid, GraphNodeBase node) {
    if (inSession && 
	oldid != null &&
	node != null) { 
      String newid = node.getId();
      compIdMap.put(oldid, newid);
      compMap.put(newid, node);
      nodes.add(node);
    }
  }

  public static void addGraphEdge(GraphEdgeBase edge) {
    if (inSession && 
	edge != null) {
      edges.add(edge);
    }
  }  

  public static void addClonedNodes(GraphDocument graph) { 
    if (inSession && 
	graph != null) { 
      Iterator<GraphNodeBase> iter = nodes.iterator(); 
      while (iter.hasNext()) { 	
	GraphNodeBase node = iter.next();
	if (node != null) { 
	  Subgraph parent = node.getSubgraph();
	  String pid = parent.getId(); 
	  String newpid = compIdMap.get(pid); 
	  if (newpid != null) { 
	    GraphNodeBase p1 = compMap.get(newpid); 
	    if (p1 instanceof Subgraph) { 
	      Subgraph newp = (Subgraph) p1; 
	      newp.addNode(node); 
	    } else { 
	      graph.addNode(node);
	    }
	  } else { 
	    parent.addNode(node); 
	  }
	}
      }
    }
  }

  public static void resolveClonedReferences(GraphDocument graph) { 
    if (inSession && 
	graph != null) { 
      Iterator<GraphEdgeBase> iter = edges.iterator(); 
      while (iter.hasNext()) { 	
	GraphEdgeBase edge = iter.next();
	if (edge != null) { 
	  ModelElement model = edge.getModel();
	  if (model instanceof AssociationElement) { 
	    AssociationElement assoc = (AssociationElement) model;
	    ReferenceElement left = assoc.getLeftReference();
	    if (left != null) { 
	      String refid = left.getRefid();
	      String newid = modelIdMap.get(refid); 
	      if (newid != null) { 
		ModelElement leftModel = modelMap.get(newid); 
		//left.setReference(leftModel);
		assoc.setLeftElement(leftModel);
	      } else {
		// the left element is not cloned 
		assoc.setLeftElement(null);
	      }
	    }
	    ReferenceElement right = assoc.getRightReference();
	    if (right != null) { 
	      String refid = right.getRefid();
	      String newid = modelIdMap.get(refid); 
	      if (newid != null) { 
		ModelElement rightModel = modelMap.get(newid); 
		//right.setReference(rightModel);
		assoc.setRightElement(rightModel);
	      } else { 
		// the right element is not cloned 
		assoc.setRightElement(null);
	      }
	    }

	    GraphNodeBase src = (GraphNodeBase) edge.getSource();
	    GraphNodeBase des = (GraphNodeBase) edge.getDestination();
	    if (src != null) { 
	      String srcid = src.getId();
	      String newid = compIdMap.get(srcid);
	      if (newid != null) { 
		GraphNodeBase newsrc = compMap.get(newid);
		Graph gcomp = graph.getGraphComponent();
		gcomp.disconnectSource(edge);
		gcomp.connectSource(edge, newsrc);
		//edge.setSource(newsrc);
	      } else { 
		// src is not cloned 
		edge.setSource(null);
	      }
	    }
	    if (des != null) { 
	      String desid = des.getId();
	      String newid = compIdMap.get(desid);
	      if (newid != null) { 
		GraphNodeBase newdes = compMap.get(newid);
		Graph gcomp = graph.getGraphComponent();
		gcomp.disconnectDestination(edge);
		gcomp.connectDestination(edge, newdes);
		//edge.setDestination(newdes);
	      } else { 
		// des is not cloned 
		edge.setDestination(null);
	      }
	    }
	  }
	}
      }
    }
  }
  
  protected static boolean inSession = false;
  
  // old id -> new id
  protected static Map<String, String> compIdMap = new HashMap<String, String>();
  protected static Map<String, String> modelIdMap = new HashMap<String, String>();
  
  // new id -> shape 
  protected static Map<String, GraphNodeBase> compMap = new HashMap<String, GraphNodeBase>();
  protected static Map<String, ModelElement> modelMap = new HashMap<String, ModelElement>();

  protected static Set<GraphEdgeBase> edges = new HashSet<GraphEdgeBase>();
  protected static Set<GraphNodeBase> nodes = new HashSet<GraphNodeBase>();
  
  private CloneManager() {} 
  
}
